Skip to contentMethod: evaluateWinningChance(List, TicTacToeFieldState)
      1: package evaluation;
2: 
3: import java.util.ArrayList;
4: import java.util.List;
5: import java.util.Set;
6: 
7: import de.fhdw.gaming.ipspiel21.searchtrees.domain.Evaluation;
8: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeBoard;
9: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeField;
10: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeFieldState;
11: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToePlayer;
12: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToePosition;
13: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeRow;
14: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeState;
15: 
16: /**
17:  * Evaluation function for a Tic-Tac-Toe Game.
18:  *
19:  * @param <S>
20:  * @param <P>
21:  */
22: public class TicTacToeEvaluation<P extends TicTacToePlayer, S extends TicTacToeState>
23:         implements Evaluation<TicTacToePlayer, TicTacToeState> {
24:     /**
25:      * Evaluates the board situation from the side of the current player.
26:      *
27:      * @param state The current game state.
28:      * @return The evaluated score.
29:      */
30: 
31:     @Override
32:     public Double evaluate(final TicTacToePlayer player, final TicTacToeState state) {
33: 
34:         double crossPlayerSum = 0;
35:         double noughtPlayerSum = 0;
36: 
37:         crossPlayerSum += this.evaluateUniformlyMarkedRows(state.getBoard(), TicTacToeFieldState.CROSS);
38:         noughtPlayerSum += this.evaluateUniformlyMarkedRows(state.getBoard(), TicTacToeFieldState.NOUGHT);
39: 
40:         if (crossPlayerSum != 100 && noughtPlayerSum != 100) {
41: 
42:             crossPlayerSum += this.evaluateMiddle(state.getBoard(), TicTacToeFieldState.CROSS);
43:             noughtPlayerSum += this.evaluateMiddle(state.getBoard(), TicTacToeFieldState.NOUGHT);
44: 
45:             crossPlayerSum += this.evaluateCorners(state.getBoard(), TicTacToeFieldState.CROSS);
46:             noughtPlayerSum += this.evaluateCorners(state.getBoard(), TicTacToeFieldState.NOUGHT);
47: 
48:             crossPlayerSum += this.evaluateNextToMids(state.getBoard(), TicTacToeFieldState.CROSS);
49:             noughtPlayerSum += this.evaluateNextToMids(state.getBoard(), TicTacToeFieldState.NOUGHT);
50: 
51:             crossPlayerSum += this.evaluateWinningChances(state.getBoard(), TicTacToeFieldState.CROSS);
52:             noughtPlayerSum += this.evaluateWinningChances(state.getBoard(), TicTacToeFieldState.NOUGHT);
53: 
54:         }
55: 
56:         if (player.isUsingCrosses()) {
57:             return crossPlayerSum - noughtPlayerSum;
58:         } else {
59:             return noughtPlayerSum - crossPlayerSum;
60: 
61:         }
62:     }
63: 
64:     /**
65:      * Evaluates the middle field of the board. The evaluated score is 4, when usedMark equals the state of the middle
66:      * field. Otherwise it is 0;
67:      *
68:      * @param board    The game board.
69:      * @param usedMark The player mark to be evaluated. Either cross or nought.
70:      * @return The evaluated score.
71:      */
72:     private double evaluateMiddle(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
73:         if (board.getFieldAt(TicTacToePosition.of(1, 1)).getState().equals(usedMark)) {
74:             return 4;
75:         } else {
76:             return 0;
77:         }
78:     }
79: 
80:     /**
81:      * Evaluates the four corner fields of the board.
82:      *
83:      * @param board    The game board.
84:      * @param usedMark The player mark to be evaluated. Either cross or nought.
85:      * @return The evaluated score.
86:      */
87:     private double evaluateCorners(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
88:         double cornerValue = 0;
89: 
90:         cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(0, 0)), usedMark);
91:         cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(0, 2)), usedMark);
92:         cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(2, 0)), usedMark);
93:         cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(2, 2)), usedMark);
94: 
95:         return cornerValue;
96:     }
97: 
98:     /**
99:      * Evaluates a corner field of the board. The evaluated score is 3, when usedMark equals the state of the corner
100:      * field. Otherwise it is 0;
101:      *
102:      * @param field    The field that is evaluated.
103:      * @param usedMark The player mark to be evaluated. Either cross or nought.
104:      * @return The evaluated score.
105:      */
106:     private double evaluateCorner(final TicTacToeField field, final TicTacToeFieldState usedMark) {
107:         if (field.getState().equals(usedMark)) {
108:             return 3;
109:         } else {
110:             return 0;
111:         }
112:     }
113: 
114:     /**
115:      * Evaluates the four fields that are next to the middle field of the board.
116:      *
117:      * @param board    The game board.
118:      * @param usedMark The player mark to be evaluated. Either cross or nought.
119:      * @return The evaluated score.
120:      */
121:     private double evaluateNextToMids(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
122:         double nexttoMidValue = 0;
123: 
124:         nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(0, 1)), usedMark);
125:         nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(1, 0)), usedMark);
126:         nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(1, 2)), usedMark);
127:         nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(2, 1)), usedMark);
128: 
129:         return nexttoMidValue;
130:     }
131: 
132:     /**
133:      * Evaluates a field that is next to the middle field of the board. The evaluated score is 2, when usedMark equals
134:      * the state of the corner field. Otherwise it is 0;
135:      *
136:      * @param field    The field that is evaluated.
137:      * @param usedMark The player mark to be evaluated. Either cross or nought.
138:      * @return The evaluated score.
139:      */
140:     private double evaluateNexttoMid(final TicTacToeField field, final TicTacToeFieldState usedMark) {
141:         if (field.getState().equals(usedMark)) {
142:             return 2;
143:         } else {
144:             return 0;
145:         }
146:     }
147: 
148:     /**
149:      * Evaluates uniformly marked rows of the board. The evaluated score is 100, when there is a uniformly marked row
150:      * and this row state equals the usedMark. Otherwise it is 0;
151:      *
152:      * @param board    The game board.
153:      * @param usedMark The player mark to be evaluated. Either cross or nought.
154:      * @return The evaluated score.
155:      */
156:     private double evaluateUniformlyMarkedRows(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
157:         final Set<TicTacToeRow> rows = board.getRowsUniformlyMarked();
158:         if (!rows.isEmpty() && rows.iterator().next().getState().isPresent()
159:                 && rows.iterator().next().getState().get().equals(usedMark)) {
160:             return 100;
161:         } else {
162:             return 0;
163:         }
164:     }
165: 
166:     /**
167:      * Evaluates all rows, columns and diagonals by possible winning chances.
168:      *
169:      * @param board    The game board.
170:      * @param usedMark The player mark to be evaluated. Either cross or nought.
171:      * @return The evaluated score.
172:      */
173:     private double evaluateWinningChances(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
174:         double sum = 0;
175:         final List<List<? extends TicTacToeField>> list = new ArrayList<>();
176:         list.add(this.getRowOfBoard(board, 0));
177:         list.add(this.getRowOfBoard(board, 1));
178:         list.add(this.getRowOfBoard(board, 2));
179:         list.add(this.getColumnOfBoard(board, 0));
180:         list.add(this.getColumnOfBoard(board, 1));
181:         list.add(this.getColumnOfBoard(board, 2));
182:         list.add(this.getDiagonalOfBoard(board, true));
183:         list.add(this.getDiagonalOfBoard(board, false));
184: 
185:         for (final List<? extends TicTacToeField> current : list) {
186:             sum += this.evaluateWinningChance(current, usedMark);
187:         }
188:         return sum;
189:     }
190: 
191:     /**
192:      * Gets a row of the board by index.
193:      *
194:      * @param board The game board.
195:      * @param index The index of the row.
196:      * @return The List of fields, that are in the row.
197:      */
198:     private List<? extends TicTacToeField> getRowOfBoard(final TicTacToeBoard board, final int index) {
199:         return board.getFields().get(index);
200:     }
201: 
202:     /**
203:      * Gets a column of the board by index.
204:      *
205:      * @param board The game board.
206:      * @param index The index of the column.
207:      * @return The List of fields, that are in the column.
208:      */
209:     private List<? extends TicTacToeField> getColumnOfBoard(final TicTacToeBoard board, final int index) {
210:         final List<TicTacToeField> list = new ArrayList<>();
211:         for (int i = 0; i < 3; i++) {
212:             list.add(board.getFieldAt(TicTacToePosition.of(i, index)));
213:         }
214:         return list;
215:     }
216: 
217:     /**
218:      * Gets a diagonal of the board.
219:      *
220:      * @param board                The game board.
221:      * @param topLeftToBottomRight When true, it refers to the diagonal from the top left corner to the bottom right
222:      *                             corner. When false, it refers to the other diagonal.
223:      * @return The List of fields, that are in the referred diagonal.
224:      */
225:     private List<? extends TicTacToeField> getDiagonalOfBoard(final TicTacToeBoard board,
226:             final boolean topLeftToBottomRight) {
227:         final List<TicTacToeField> list = new ArrayList<>();
228:         if (topLeftToBottomRight) {
229:             for (int i = 0; i < 3; i++) {
230:                 list.add(board.getFieldAt(TicTacToePosition.of(i, i)));
231:             }
232:         } else {
233:             list.add(board.getFieldAt(TicTacToePosition.of(2, 0)));
234:             list.add(board.getFieldAt(TicTacToePosition.of(1, 1)));
235:             list.add(board.getFieldAt(TicTacToePosition.of(0, 2)));
236:         }
237:         return list;
238:     }
239: 
240:     /**
241:      * Evaluates a winning chance. There is a possible winning chance, when two of the three field states equal the
242:      * usedMark and the last field state equals empty. When there is a possible winning chance the evaluated score is
243:      * 10. Otherwise it is 0;
244:      *
245:      * @param fields   A list of the fields to evaluate the possible winning chance.
246:      * @param usedMark The player mark to be evaluated. Either cross or nought.
247:      * @return The evaluated score.
248:      */
249:     private double evaluateWinningChance(final List<? extends TicTacToeField> fields,
250:             final TicTacToeFieldState usedMark) {
251:         int usedMarks = 0;
252:         int emptys = 0;
253:•        for (final TicTacToeField field : fields) {
254:•            if (field.getState().equals(usedMark)) {
255:                 usedMarks += 1;
256:•            } else if (field.getState().equals(TicTacToeFieldState.EMPTY)) {
257:                 emptys += 1;
258:             }
259:         }
260: 
261:•        if (usedMarks == 2 && emptys == 1) {
262:             return 10;
263:         } else {
264:             return 0;
265:         }
266:     }
267: 
268: }